package cn.zyq.charging.cost.controller;

import cn.zyq.charging.common.pojo.JsonResult;
import cn.zyq.charging.common.pojo.param.ChargingProcessParam;
import cn.zyq.charging.common.pojo.vo.ChargingProcessVO;
import cn.zyq.charging.cost.service.CostService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

//http://localhost:4049/doc.html

/**
 * 计价服务控制器
 * @author yongqi.zhao
 */
@Slf4j
@RequestMapping("/cost")
@RestController
@Api(tags="计价服务")
public class CostController {

    @Autowired(required = false)
    CostService costService;

    /**
     * 根据充电进度信息计算充电费用。
     * 具体代码逻辑过程如下：
     * 1.controller： 调用service层calculateCost(chargingProcessParam)方法计算，将结果返回给前端。
     * 2.serviceImpl： calculateCost()中
     *   2.1.用buildChargingData(ChargingProcessParam)根据orderNo从项目中的一个HashMap集合中获取本订单的充电数据对象ChargingData对象，
     *      用buildChargingData()方法中：如果获取到chargingData对象就返回，从HashMap集合如果获取不到chargingData对象就构建一个放入到HashMap集合中并返回。
     *   2.2.然后用getMatchCostRuleByTime(stationId<来自方法参数chargingProcessParam>, hour<用apiLocalDateTime.now().getHour()获取>)获取充电规则。
     *      2.2.1.getMatchCostRuleByTime()中调用getCostRule(stationId)获取这个充电站所有的计价规则
     *          2.2.1.1.getCostRule(stationId)中，先从redis中获取计价规则列表(key对应的value是一个List)，costRuleCacheRepository.getCostRuleByStationId(stationId);
     *          如果redis中找到计划规则列表数据，就返回计价规则列表。
     *             2.2.1.1.1.getCostRuleByStationId()中调用getCostRulePOS()方法，利用fastjackson的api将json字符串转化为List<LinkedHashMap<String,CostRulePO>>，然后再将LinkedHashMap转化为CostPO存入到List返回
     *          2.2.1.2.getCostRule(stationId)中，如果从redis没获取到计价规则数据就从mysql数据库中查询。 如果从mysql数据库中查询到了计价规则数据就再存入到redis中，costRuleCacheRepository.saveCostRule(stationId,dbCostRules);
     *          并返回计价规则列表。  如果没查询到计划规则数据就返回空集合。
     *      2.2.2.getMatchCostRuleByTime()中再判断当前时间处于哪个计价规则的startTime和endTime之间，匹配到对应的计价规则后就返回这个计价规则。 没匹配到就返回null。
     *   2.3.如果获取到了计价规则，就从计价规则中获取电费单价设置给ChargingData对象，然后从本次充电数据中获取上一次充电数据。
     *       2.3.1.如果上次充电数据不存在就调用appendFirstCostToChargingData(chargingProcessParam, chargingData, powerFee, hour, ruleName);
     *      计算第一次充电费用(chargingProcessParam获取充电总度数chargingCapacity，然后 用单价*充电总度数 powerFee.multiply(chargingCapacityBigDecimal))。
     *      再将充电总费用设置到ChargingData对象中。
     *
     *      2.3.2.如果上次充电数据存在就调用appendLastCostToChargingData(chargingProcessParam, chargingData, powerFee, hour, ruleName);
     *      计算本次的充电费用  ：
     *        appendLastCostToChargingData中：
     *           A.chargingProcessParam获取充电总度数chargingCapacity，然后 用单价*充电总度数计算本次充电费用 powerFee.multiply(chargingCapacityBigDecimal)。
     *           B.然后再从chargingData中获取之前的充电总费用 chargingData.getTotalCost() ， 然后用本次充电费用加上之前的充电总费用。totalCost.add(currentChargingCost)
     *           C.将充电总费用设置到chargingData对象中。
     *           D.将本次充电进度数据chargingProcessParam设置到chargingData对象中，作为最后一次充电进度记录数据。
     *   2.4.如果获取不到计价规则数据就返回null，并用log.error打印异常信息。
     *   2.5.将本次充电总进度数据设置到chargingData对象中。chargingData.setTotalCapacity(chargingProcessParam.getChargingCapacity());
     *   2.6.根据chargingData中的信息构建一个ChargingProcessVO对象返回给前端。
     *   根据充电进度信息获取充电规则，根据规则计算充电费用。
     * @param chargingProcessParam
     * @return JsonResult
     */
    @ApiOperation("计算费用")
    @PostMapping("/calculate")
    public JsonResult calculateCost(@RequestBody ChargingProcessParam chargingProcessParam){
        //@RequestBody是来自于spring框架的Restful风格，在Post或Put请求传递参数时需要用此注解获取参数
        log.debug("计算充电费用calculateCost()--入参-{}",chargingProcessParam);
        ChargingProcessVO chargingProcessVO=costService.calculateCost(chargingProcessParam);
        log.debug("计算充电费用成功calculateCost()--入参-{}，--出参--{}",chargingProcessParam,chargingProcessVO);
        return JsonResult.ok(chargingProcessVO);
    }
    //方法入参用dto(服务之间传输数据比如订单和设备) , controller类的方法的入参用param结尾的对象(用于前端和java后端传输)
    //controller方法给前端页面返回数据用vo(用于页面展示)

}


